﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace isky.webapi
{
    public partial class CRMWebAPI
    {


        public async Task<CRMBatchResult> Create(string entityCollection, object[] datalist)
        {
            var result = await this.Create(entityCollection, datalist, this._httpClient);
            return result;

        }


        /// <summary>
        /// create multiple records at once using a batch
        /// </summary>
        /// <param name="entityCollection"></param>
        /// <param name="datalist"></param>
        /// <returns></returns>
        public async Task<CRMBatchResult> Create(string entityCollection, object[] datalist, HttpClient httpClient, Guid callerID = default(Guid))
        {
            await CheckAuthToken();

            httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
            httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
            httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
            // _httpClient.DefaultRequestHeaders.Add("Prefer", "odata.maxpagesize=3");
            if (callerID != Guid.Empty)
                httpClient.DefaultRequestHeaders.Add("MSCRMCallerID", callerID.ToString());

            httpClient.Timeout = new TimeSpan(0, 2, 0);

            var batchid = "batch_" + Guid.NewGuid().ToString();

            MultipartContent batchContent = new MultipartContent("mixed", batchid);
            var changesetID = "changeset_" + Guid.NewGuid().ToString();
            MultipartContent changeSetContent = new MultipartContent("mixed", changesetID);

            int contentID = 1;
            foreach (var data in datalist)
            {
                HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, _apiUrl + entityCollection);

                req.Content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
                HttpMessageContent content = new HttpMessageContent(req);
                content.Headers.Remove("Content-Type");
                content.Headers.TryAddWithoutValidation("Content-Type", "application/http");
                content.Headers.TryAddWithoutValidation("Content-Transfer-Encoding", "binary");
                content.Headers.TryAddWithoutValidation("Content-ID", contentID.ToString());
                contentID++;
                changeSetContent.Add(content);
            }

            batchContent.Add(changeSetContent);

            HttpRequestMessage batchRequest = new HttpRequestMessage(HttpMethod.Post, _apiUrl + "$batch");

            batchRequest.Content = batchContent;

            var batchstring = await batchRequest.Content.ReadAsStringAsync();

            var response = await httpClient.SendAsync(batchRequest);
            var responseString = response.Content.ReadAsStringAsync();
            MultipartMemoryStreamProvider batchStream = await response.Content.ReadAsMultipartAsync(); ;
            var changesetStream = batchStream.Contents.FirstOrDefault();

            StreamContent changesetFixedContent = FixupChangeStreamDueToBug(changesetStream);

            var changesetFixedStream = await changesetFixedContent.ReadAsMultipartAsync();
            CRMBatchResult finalResult = new CRMBatchResult();
            finalResult.ResultItems = new List<CRMBatchResultItem>();

            foreach (var responseContent in changesetFixedStream.Contents)
            {
                var fixedREsponseContent = FixupToAddCorrectHttpContentType(responseContent);
                var individualResponseString = await fixedREsponseContent.ReadAsStringAsync();
                var indivdualResponse = await fixedREsponseContent.ReadAsHttpResponseMessageAsync();
                if (!indivdualResponse.IsSuccessStatusCode)
                {
                    var jsonData = JsonConvert.SerializeObject(datalist);
                    EnsureSuccessStatusCode(indivdualResponse, jsonData: jsonData);
                }
                var idString = indivdualResponse.Headers.GetValues("OData-EntityId").FirstOrDefault();
                idString = idString.Replace(_apiUrl + entityCollection, "").Replace("(", "").Replace(")", "");
                CRMBatchResultItem resultItem = new CRMBatchResultItem();
                resultItem.EntityID = Guid.Parse(idString);
                finalResult.ResultItems.Add(resultItem);
            }

            return finalResult;
        }

        /// <summary>
        /// Batch Update
        /// </summary>
        /// <param name="entityCollection">实体复数名</param>
        /// <param name="datalist">跟新记录集合</param>
        /// <param name="keyName">主键名称</param>
        /// <param name="httpClient"></param>
        /// <param name="callerID">模拟用户</param>
        /// <returns></returns>
        public async Task<CRMBatchResult> Update(string entityCollection, object[] datalist, string keyName, HttpClient httpClient, Guid callerID = default(Guid))
        {
            httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
            httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
            httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");

            if (callerID != Guid.Empty)
                httpClient.DefaultRequestHeaders.Add("MSCRMCallerID", callerID.ToString());

            httpClient.Timeout = new TimeSpan(0, 2, 0);
            var batchid = "batch_" + Guid.NewGuid().ToString();

            MultipartContent batchContent = new MultipartContent("mixed", batchid);
            var changesetID = "changeset_" + Guid.NewGuid().ToString();
            MultipartContent changeSetContent = new MultipartContent("mixed", changesetID);
            var fullUrl = _apiUrl + entityCollection;
            int contentID = 1;
            var updateReqContents = datalist.Select(data =>
            {
                var dataIndexer = data as IDictionary<string, Object>;
                if (dataIndexer != null)
                {
                    HttpRequestMessage req = new HttpRequestMessage(new HttpMethod("PATCH"), fullUrl + $"({dataIndexer[keyName].ToString()})");

                    req.Content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
                    HttpMessageContent content = new HttpMessageContent(req);
                    content.Headers.Remove("Content-Type");
                    content.Headers.TryAddWithoutValidation("Content-Type", "application/http");
                    content.Headers.TryAddWithoutValidation("Content-Transfer-Encoding", "binary");
                    content.Headers.TryAddWithoutValidation("Content-ID", contentID.ToString());
                    content.Headers.TryAddWithoutValidation("If-Match", "*");
                    contentID++;
                    return content;
                }
                else
                {
                    return null;
                }
            });

            foreach (var updateReqContent in updateReqContents)
            {
                changeSetContent.Add(updateReqContent);
            }
            batchContent.Add(changeSetContent);


            HttpRequestMessage batchRequest = new HttpRequestMessage(HttpMethod.Post, _apiUrl + "$batch");

            batchRequest.Content = batchContent;

            var batchstring = await batchRequest.Content.ReadAsStringAsync();

            var response = await httpClient.SendAsync(batchRequest);


            var responseString = response.Content.ReadAsStringAsync();
            MultipartMemoryStreamProvider batchStream = await response.Content.ReadAsMultipartAsync(); ;
            var changesetStream = batchStream.Contents.FirstOrDefault();

            StreamContent changesetFixedContent = FixupChangeStreamDueToBug(changesetStream);

            var changesetFixedStream = await changesetFixedContent.ReadAsMultipartAsync();
            CRMBatchResult finalResult = new CRMBatchResult();
            finalResult.ResultItems = new List<CRMBatchResultItem>();

            foreach (var responseContent in changesetFixedStream.Contents)
            {
                var fixedREsponseContent = FixupToAddCorrectHttpContentType(responseContent);
                var individualResponseString = await fixedREsponseContent.ReadAsStringAsync();
                var indivdualResponse = await fixedREsponseContent.ReadAsHttpResponseMessageAsync();
                if (!indivdualResponse.IsSuccessStatusCode)
                {
                    var jsonData = JsonConvert.SerializeObject(datalist);
                    EnsureSuccessStatusCode(indivdualResponse, jsonData: jsonData);
                }
                var idString = indivdualResponse.Headers.GetValues("OData-EntityId").FirstOrDefault();
                idString = idString.Replace(_apiUrl + entityCollection, "").Replace("(", "").Replace(")", "");
                CRMBatchResultItem resultItem = new CRMBatchResultItem();

                //if alternate key was used to perform an upsert, guid not currently returned
                //the call returns the alternate key which is not in guid format
                var idGuid = Guid.Empty;
                if (Guid.TryParse(idString, out idGuid))
                {
                    resultItem.EntityID = idGuid;
                }
                else
                {
                    resultItem.ElternateKey = idString;
                }
                finalResult.ResultItems.Add(resultItem);
            }

            return finalResult;

        }

        /// <summary>
        /// Upsert multiple records at once using a batch
        /// </summary>
        /// <param name="entityCollection"></param>
        /// <param name="datalist"></param>
        /// <returns></returns>
        public async Task<CRMBatchResult> Upsert(string entityCollection, object[] datalist, HttpClient httpClient, Guid callerID = default(Guid), params string[] keyNames)
        {
            httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
            httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
            httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");

            if (callerID != Guid.Empty)
                httpClient.DefaultRequestHeaders.Add("MSCRMCallerID", callerID.ToString());

            httpClient.Timeout = new TimeSpan(0, 2, 0);

            var batchid = "batch_" + Guid.NewGuid().ToString();

            MultipartContent batchContent = new MultipartContent("mixed", batchid);
            var changesetID = "changeset_" + Guid.NewGuid().ToString();
            MultipartContent changeSetContent = new MultipartContent("mixed", changesetID);
            var fullUrl = _apiUrl + entityCollection;

            int contentID = 1;
            foreach (var data in datalist)
            {
                var dataIndexer = data as IDictionary<string, Object>;
                string keyNameStr = string.Empty;
                for (int i = 0; i < keyNames.Length; i++)
                {
                    if (!dataIndexer.Keys.Contains(keyNames[i])) continue;
                    keyNameStr += $"{keyNames[i]}='{dataIndexer[keyNames[i]]}'";
                    if (i != keyNames.Length - 1)
                    {
                        keyNameStr += ",";
                    }
                }
                HttpRequestMessage req = new HttpRequestMessage(new HttpMethod("PATCH"), fullUrl + $"({keyNameStr})");

                req.Content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
                HttpMessageContent content = new HttpMessageContent(req);
                content.Headers.Remove("Content-Type");
                content.Headers.TryAddWithoutValidation("Content-Type", "application/http");
                content.Headers.TryAddWithoutValidation("Content-Transfer-Encoding", "binary");
                content.Headers.TryAddWithoutValidation("Content-ID", contentID.ToString());
                contentID++;
                changeSetContent.Add(content);
            }

            batchContent.Add(changeSetContent);

            HttpRequestMessage batchRequest = new HttpRequestMessage(HttpMethod.Post, _apiUrl + "$batch");

            batchRequest.Content = batchContent;

            var batchstring = await batchRequest.Content.ReadAsStringAsync();

            var response = await httpClient.SendAsync(batchRequest);


            var responseString = response.Content.ReadAsStringAsync();
            MultipartMemoryStreamProvider batchStream = await response.Content.ReadAsMultipartAsync(); ;
            var changesetStream = batchStream.Contents.FirstOrDefault();

            StreamContent changesetFixedContent = FixupChangeStreamDueToBug(changesetStream);

            var changesetFixedStream = await changesetFixedContent.ReadAsMultipartAsync();
            CRMBatchResult finalResult = new CRMBatchResult();
            finalResult.ResultItems = new List<CRMBatchResultItem>();

            foreach (var responseContent in changesetFixedStream.Contents)
            {
                var fixedREsponseContent = FixupToAddCorrectHttpContentType(responseContent);
                var individualResponseString = await fixedREsponseContent.ReadAsStringAsync();
                var indivdualResponse = await fixedREsponseContent.ReadAsHttpResponseMessageAsync();
                if (!indivdualResponse.IsSuccessStatusCode)
                {
                    var jsonData = JsonConvert.SerializeObject(datalist);
                    EnsureSuccessStatusCode(indivdualResponse, jsonData: jsonData);
                }
                var idString = indivdualResponse.Headers.GetValues("OData-EntityId").FirstOrDefault();
                idString = idString.Replace(_apiUrl + entityCollection, "").Replace("(", "").Replace(")", "");
                CRMBatchResultItem resultItem = new CRMBatchResultItem();

                //if alternate key was used to perform an upsert, guid not currently returned
                //the call returns the alternate key which is not in guid format
                var idGuid = Guid.Empty;
                if (Guid.TryParse(idString, out idGuid))
                {
                    resultItem.EntityID = idGuid;
                }
                else
                {
                    resultItem.ElternateKey = idString;
                }
                finalResult.ResultItems.Add(resultItem);
            }

            return finalResult;
        }

        /// <summary>
        /// delete multiple records at once using a batch
        /// </summary>
        /// <param name="entityCollection"></param>
        /// <param name="datalist"></param>
        /// <returns></returns>
        public async Task<CRMBatchResult> Delete(string entityCollection, Guid[] entityIDs, HttpClient httpClient)
        {
            httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
            httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
            httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");


            httpClient.Timeout = new TimeSpan(0, 2, 0);

            var batchid = "batch_" + Guid.NewGuid().ToString();

            MultipartContent batchContent = new MultipartContent("mixed", batchid);
            var changesetID = "changeset_" + Guid.NewGuid().ToString();
            MultipartContent changeSetContent = new MultipartContent("mixed", changesetID);

            int contentID = 1;
            foreach (var entityID in entityIDs)
            {
                HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Delete, _apiUrl + entityCollection + "(" + entityID.ToString() + ")");
                HttpMessageContent content = new HttpMessageContent(req);
                content.Headers.Remove("Content-Type");
                content.Headers.TryAddWithoutValidation("Content-Type", "application/http");
                content.Headers.TryAddWithoutValidation("Content-Transfer-Encoding", "binary");
                content.Headers.TryAddWithoutValidation("Content-ID", contentID.ToString());
                contentID++;
                changeSetContent.Add(content);
            }

            batchContent.Add(changeSetContent);

            HttpRequestMessage batchRequest = new HttpRequestMessage(HttpMethod.Post, _apiUrl + "$batch");

            batchRequest.Content = batchContent;

            var batchstring = await batchRequest.Content.ReadAsStringAsync();

            var response = await httpClient.SendAsync(batchRequest);
            var responseString = response.Content.ReadAsStringAsync();
            MultipartMemoryStreamProvider batchStream = await response.Content.ReadAsMultipartAsync(); ;
            var changesetStream = batchStream.Contents.FirstOrDefault();

            StreamContent changesetFixedContent = FixupChangeStreamDueToBug(changesetStream);

            var changesetFixedStream = await changesetFixedContent.ReadAsMultipartAsync();
            CRMBatchResult finalResult = new CRMBatchResult();
            finalResult.ResultItems = new List<CRMBatchResultItem>();

            foreach (var responseContent in changesetFixedStream.Contents)
            {
                var fixedREsponseContent = FixupToAddCorrectHttpContentType(responseContent);
                var individualResponseString = await fixedREsponseContent.ReadAsStringAsync();
                var indivdualResponse = await fixedREsponseContent.ReadAsHttpResponseMessageAsync();
                var idString = indivdualResponse.ReasonPhrase;
                CRMBatchResultItem resultItem = new CRMBatchResultItem();
                resultItem.ElternateKey = idString;
                finalResult.ResultItems.Add(resultItem);
            }

            return finalResult;
        }
    }
}
